package com.jaws.core.mvc.token;

import java.lang.reflect.Method;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.ModelAndView;

import com.jaws.core.common.log.Log;
import com.jaws.core.common.log.LogOp;
import com.jaws.core.common.vo.OpResponse;
import com.jaws.core.mvc.exception.JawsMvcExceptionCode;

/**
 * token生成和验证
 * 
 * @author maojian
 * @date 2017-05-16 10:46:05
 * @since v1.0.0
 */
public class FormTokenAspect {

	private static final Logger logger = LoggerFactory.getLogger(FormTokenAspect.class);

	@Resource
	TokenManager formTokenManager;

	@Autowired
	HttpServletRequest request;

	public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
		// 获取token
		MethodSignature ms = (MethodSignature) pjp.getSignature();
		Method method = ms.getMethod();
		FormToken formToken = method.getAnnotation(FormToken.class);
		if (formToken == null) {
			return pjp.proceed();
		}
		// 检验token
		if (formToken.checkToken()) {
			String token = request.getParameter(TokenManager.TOKEN);
			if (StringUtils.isBlank(token)) {
				logger.error(Log.op(LogOp.TOKEN_CHECK).msg("request token is empty").toString());
				throw JawsMvcExceptionCode.TOKEN_CHECK_ERROR.exp();
			}
			boolean checked = formTokenManager.checkAndDelToken(token);
			if (!checked) {
				logger.error(Log.op(LogOp.TOKEN_CHECK).msg("token check error").kv("request token", token).kv("real token", formTokenManager.getToken(token)).toString());
				throw JawsMvcExceptionCode.TOKEN_CHECK_ERROR.exp();
			}
			// 如果只配置了checkToken，但是没配置generateToken，如果返回的是业务异常的话，也需要重新返回token
			// 跟直接配置@FormToken(checkToken=true, generateToken=true)相比，会少生成一些token，节省内存
			if (!formToken.generateToken()) {
				return processNewToken(pjp, true);
			}
		}
		// 设置token
		if (formToken.generateToken()) {
			return processNewToken(pjp, false);
		}
		return pjp.proceed();
	}

	/**
	 * 生成新的token
	 * 
	 * @param pjp
	 * @param checkCode 是否判断返回码，false则不判断直接生成token，true则判断到为错误码的时候才生成token
	 * @return
	 * @throws Throwable
	 */
	private Object processNewToken(ProceedingJoinPoint pjp, boolean checkCode) throws Throwable {
		String token = formTokenManager.newToken();
		Object retValue = pjp.proceed();
		if (retValue instanceof OpResponse) { // json
			OpResponse op = (OpResponse) retValue;
			if (!checkCode) {
				op.setToken(token);
			} else if (isFailureCode(String.valueOf(op.getCode()))) {
				op.setToken(token);
			}
		}
		if (retValue instanceof ModelAndView) { // 页面
			ModelAndView op = (ModelAndView) retValue;
			if (!checkCode) {
				op.addObject(TokenManager.TOKEN, token);
			} else if (isFailureCode(String.valueOf(op.getModel().get("code")))) {
				op.addObject(TokenManager.TOKEN, token);
			}
		} else {
			// 如果是返回到页面，则把token放到request.attribute中
			request.setAttribute(TokenManager.TOKEN, token);
		}
		return retValue;
	}

	/**
	 * 判断返回的code是否是错误码
	 * 
	 * @param code
	 * @return
	 */
	private boolean isFailureCode(String code) {
		if (formTokenManager.getSucCodeList() == null) {
			return true;
		}
		return !formTokenManager.getSucCodeList().contains(code);
	}

	public void setFormTokenManager(TokenManager formTokenManager) {
		this.formTokenManager = formTokenManager;
	}

	public void setRequest(HttpServletRequest request) {
		this.request = request;
	}

}